import os
import re
import tkinter as tk
import webbrowser
import tempfile
import sys
from tkinter import ttk, scrolledtext, filedialog, messagebox, simpledialog
import uuid
import hashlib
import psutil  # 硬件信息依赖
from Crypto.Cipher import AES  # 加密依赖
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Util.Padding import pad, unpad
from urllib.request import urlopen
from urllib.parse import urlparse, urljoin
from html.parser import HTMLParser
from pathlib import Path
import mimetypes
import threading
import time
import json  # 新增工作站功能依赖
import importlib.util


# 序列号验证模块（核心修复版）
class LicenseManager:
    LICENSE_FILE = os.path.join(os.path.expanduser("~"), ".enhanced_html_viewer.lic")  # 类属性定义
    SALT = b'\x98\x1a\xdf\x07\xbc\x4f\x2d\x8e'
    ITERATIONS = 100000  # 密钥派生迭代次数

    @staticmethod
    def get_hardware_fingerprint():
        """增强版硬件指纹（兼容虚拟机/低权限环境）"""
        try:
            parts = []

            # 1. CPU指纹（允许虚拟机ID）
            try:
                if psutil:
                    cpu_info = psutil.cpu_info()[0]
                    cpu_serial = cpu_info.get('serial', '').encode() or b'VM_CPU'
                    parts.append(hashlib.sha256(cpu_serial).hexdigest()[:15])
            except:
                parts.append('CPU_UNKNOWN')

            # 2. 硬盘指纹（优先UUID）
            try:
                if psutil:
                    for part in psutil.disk_partitions():
                        if 'physical' in part.opts and part.uuid:
                            parts.append(hashlib.sha256(part.uuid.encode()).hexdigest()[:15])
                            break
                    else:  # 虚拟机回退
                        parts.append(hashlib.sha256(b'VM_DISK').hexdigest()[:15])
            except:
                parts.append('DISK_UNKNOWN')

            # 3. MAC地址（允许虚拟MAC）
            try:
                mac = str(uuid.getnode()).encode()
                parts.append(hashlib.sha256(mac).hexdigest()[:15])
            except:
                parts.append('MAC_UNKNOWN')

            return '-'.join(parts)
        except Exception:
            return "DEMO_MODE"  # 安全回退

    @staticmethod
    def encrypt_license(serial, password):
        """AES-256-CBC加密（带IV向量）"""
        key = PBKDF2(password, LicenseManager.SALT, dkLen=32, count=LicenseManager.ITERATIONS)  # ✅ 类属性引用
        iv = os.urandom(16)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return iv + cipher.encrypt(pad(serial.encode(), AES.block_size))

    @staticmethod
    def decrypt_license(encrypted_data, password):
        """AES解密（带IV向量）"""
        try:
            key = PBKDF2(password, LicenseManager.SALT, dkLen=32, count=LicenseManager.ITERATIONS)  # ✅ 类属性引用
            iv = encrypted_data[:16]
            ciphertext = encrypted_data[16:]
            return unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(ciphertext), AES.block_size).decode()
        except Exception:
            return None

    @staticmethod
    def verify_license(serial, fingerprint):
        """序列号验证核心逻辑"""
        clean_serial = re.sub(r"[^A-Z0-9]", "", serial.upper())
        if len(clean_serial) != 15:
            return False, "序列号格式错误（15位字母数字）"

        expected_hash = hashlib.sha256(fingerprint.encode()).hexdigest()[:15].upper()
        if clean_serial != expected_hash:
            return False, f"硬件不匹配\n设备指纹: {expected_hash}\n输入序列号: {clean_serial}"

        return True, "授权成功"

    @staticmethod
    def check_license():
        """启动时自动验证本地授权文件"""
        # ❗ 关键修复：添加类名前缀
        if os.path.exists(LicenseManager.LICENSE_FILE):  # ✅ 修正此处！
            try:
                fingerprint = LicenseManager.get_hardware_fingerprint()
                with open(LicenseManager.LICENSE_FILE, 'rb') as f:  # ✅ 修正此处！
                    encrypted = f.read()
                serial = LicenseManager.decrypt_license(encrypted, fingerprint)
                return LicenseManager.verify_license(serial, fingerprint)[0]
            except Exception:
                return False
        return False


class ResourceDownloader:
    def __init__(self, base_url, html_content, log_callback, save_dir):
        self.base_url = base_url
        self.html_content = html_content
        self.resource_dir = os.path.join(save_dir, "resources")
        os.makedirs(self.resource_dir, exist_ok=True)
        self.total_count = 0
        self.success_count = 0
        self.failure_count = 0
        self.log_callback = log_callback
        self.current_index = 0
        self.download_logs = {}
        self.current_file_percentage = 0
        self.current_file_start_time = None
        self.current_file_bytes_downloaded = 0

    def get_total_resources(self):
        patterns = [
            (r'(href|src)=["\'](.*?)["\']', lambda match: match.group(2)),
            (r'url\(["\']?(.*?)["\']?\)', lambda match: match.group(1))
        ]
        count = 0
        for pattern, extractor in patterns:
            for match in re.finditer(pattern, self.html_content):
                link = extractor(match)
                if link and not link.startswith(('data:', '#')):
                    count += 1
        return count

    def process_resources(self):
        self.total_count = self.get_total_resources()
        patterns = [
            (r'(href|src)=["\'](.*?)["\']', self._process_generic_resource),
            (r'url\(["\']?(.*?)["\']?\)', self._process_css_resource)
        ]

        modified_html = self.html_content
        for pattern, processor in patterns:
            modified_html = re.sub(pattern, processor, modified_html)
        return modified_html

    def _process_generic_resource(self, match):
        attr = match.group(1)
        original_url = match.group(2)
        self.current_index += 1
        self.log_callback(f"正在尝试下载: {original_url}", "normal")
        self.current_file_start_time = time.time()
        self.current_file_bytes_downloaded = 0
        return f'{attr}="{self._download_and_replace(original_url)}"'

    def _process_css_resource(self, match):
        original_url = match.group(1)
        self.current_index += 1
        self.log_callback(f"正在尝试下载: {original_url}", "normal")
        self.current_file_start_time = time.time()
        self.current_file_bytes_downloaded = 0
        return f'url("{self._download_and_replace(original_url)}")'

    def _download_and_replace(self, original_url):
        if not original_url or original_url.startswith(('data:', '#')):
            return original_url

        local_path = None
        for attempt in range(2):
            try:
                absolute_url = urljoin(self.base_url, original_url)
                local_path = self._download_resource(absolute_url, original_url)
                if local_path:
                    self.success_count += 1
                    self.log_callback(f"下载成功: {original_url}", "success")
                    break
            except Exception as e:
                if attempt == 0:
                    self.log_callback(f"下载失败，正在重试: {original_url}", "warning")
                else:
                    self.failure_count += 1
                    self.log_callback(f"下载失败: {original_url}", "error")

        return os.path.join("resources", local_path) if local_path else original_url

    def _download_resource(self, url, original_url):
        try:
            parsed = urlparse(url)
            filename = os.path.basename(parsed.path) or "untitled"

            response = urlopen(url)
            content_type = response.headers.get('Content-Type')
            if content_type:
                ext = mimetypes.guess_extension(content_type)
                if ext:
                    if not filename.endswith(ext):
                        if '.' not in filename:
                            filename = filename + ext
                        else:
                            base_name, _ = os.path.splitext(filename)
                            filename = base_name + ext

            if 'text/html' in content_type and not filename.endswith('.html'):
                base_name, _ = os.path.splitext(filename)
                filename = base_name + '.html'

            filename = self._sanitize_filename(filename)

            dest_path = Path(self.resource_dir) / filename
            counter = 1
            while dest_path.exists():
                stem = dest_path.stem
                new_name = f"{stem}_{counter}{dest_path.suffix}"
                dest_path = dest_path.with_name(new_name)
                counter += 1

            total_size = int(response.headers.get('Content-Length', 0))
            bytes_downloaded = 0

            with open(dest_path, 'wb') as f:
                while True:
                    data = response.read(1024)
                    if not data:
                        break
                    f.write(data)
                    bytes_downloaded += len(data)
                    self.current_file_bytes_downloaded = bytes_downloaded
                    if total_size > 0:
                        percent = (bytes_downloaded / total_size) * 100
                        self.current_file_percentage = percent
                        self.update_download_log(original_url, percent)

            return dest_path.name
        except Exception as e:
            raise

    def _sanitize_filename(self, name):
        return re.sub(r'[\\/*?:"<>|]', "_", name).strip()

    def update_download_log(self, url, percent):
        log_message = f"{url} 下载进度: {percent:.2f}%"
        if url in self.download_logs:
            start_index = self.download_logs[url]
            end_index = f"{start_index} lineend"
            self.log_callback("", "normal", start_index, end_index)
        start_index = self.log_callback(log_message, "normal")
        self.download_logs[url] = start_index


# 主程序核心（带授权验证）
class EnhancedHTMLViewer:
    def __init__(self):
        # 强制授权验证（验证失败则退出）
        if not self._run_license_check():
            return

        self.window = tk.Tk()
        self.window.title("网页编辑器 v5.1 [授权版] ✨")
        self.window.geometry("1200x800")
        self.current_workstation = None
        self._create_widgets()
        self._setup_highlight()
        self.load_plugins()
        self.setup_shortcuts()
        self.window.mainloop()

    def _run_license_check(self):
        """集成到启动流程的授权验证"""
        # 开发模式跳过（正式发布需删除）
        if os.environ.get("DEV_MODE") == "1":
            return True

        # 检查本地授权
        if LicenseManager.check_license():
            return True

        # 首次启动引导
        root = tk.Tk()
        root.withdraw()

        while True:
            serial = tk.simpledialog.askstring(
                "授权验证",
                "请输入15位序列号（格式：XXX-XXX-XXX）:\n"
                "(通过官方工具获取，硬件变更需重新申请)",
                parent=root,
                show=''  # 显示明文方便调试
            )

            if not serial:
                messagebox.showerror("授权失败", "未提供序列号", parent=root)
                return False

            fingerprint = LicenseManager.get_hardware_fingerprint()
            valid, msg = LicenseManager.verify_license(serial, fingerprint)
            if valid:
                # 保存授权文件
                encrypted = LicenseManager.encrypt_license(serial, fingerprint)
                with open(LicenseManager.LICENSE_FILE, 'wb') as f:  # ✅ 修正此处！
                    f.write(encrypted)
                messagebox.showinfo("授权成功", "欢迎使用正式版！", parent=root)
                root.destroy()
                return True
            else:
                messagebox.showerror("授权失败", msg, parent=root)

    def _create_widgets(self):
        self.log_text = scrolledtext.ScrolledText(
            self.window,
            wrap=tk.WORD,
            font=('TkFixedFont', 12),
            width=30
        )
        self.log_text.pack(side=tk.LEFT, fill=tk.Y)
        self.log_text.tag_configure("normal", foreground="black")
        self.log_text.tag_configure("error", foreground="red")
        self.log_text.tag_configure("warning", foreground="orange")
        self.log_text.tag_configure("success", foreground="green")

        main_frame = tk.Frame(self.window)
        main_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        menubar = tk.Menu(self.window)
        self.menubar = menubar  # 将 menubar 设置为类的属性

        self.file_menu = tk.Menu(menubar, tearoff=0)
        self.file_menu.add_command(label="打开网页", command=self.load_url, accelerator="Ctrl+O")
        self.window.bind_all("<Control-o>", lambda event: self.load_url())
        self.file_menu.add_command(label="保存", command=self.start_download, accelerator="Ctrl+S")
        self.window.bind_all("<Control-s>", lambda event: self.start_download())
        menubar.add_cascade(label="文件", menu=self.file_menu)

        self.run_menu = tk.Menu(menubar, tearoff=0)
        self.run_menu.add_command(label="运行", command=self.run_html, accelerator="F5")
        self.window.bind_all("<F5>", lambda event: self.run_html())
        self.run_menu.add_command(label="保存后运行", command=self.save_and_run, accelerator="Ctrl+R")
        self.window.bind_all("<Control-r>", lambda event: self.save_and_run())
        menubar.add_cascade(label="运行", menu=self.run_menu)

        self.workstation_menu = tk.Menu(menubar, tearoff=0)
        self.workstation_menu.add_command(label="保存工作站", command=self.save_workstation, accelerator="Ctrl+W")
        self.window.bind_all("<Control-w>", lambda event: self.save_workstation())
        self.workstation_menu.add_command(label="打开工作站", command=self.open_workstation, accelerator="Ctrl+Shift+W")
        self.window.bind_all("<Control-Shift-w>", lambda event: self.open_workstation())
        menubar.add_cascade(label="工作站", menu=self.workstation_menu)

        self.download_menu = tk.Menu(menubar, tearoff=0)
        self.download_menu.add_command(label="下载全部超链接文件", command=self.start_download_all_links, accelerator="Ctrl+D")
        self.window.bind_all("<Control-d>", lambda event: self.start_download_all_links())
        menubar.add_cascade(label="下载", menu=self.download_menu)

        self.settings_menu = tk.Menu(menubar, tearoff=0)
        self.settings_menu.add_command(label="设置", command=self.open_settings)
        menubar.add_cascade(label="设置", menu=self.settings_menu)

        self.window.config(menu=menubar)

        # 快捷栏
        self.shortcut_bar = tk.Frame(main_frame)
        self.shortcut_bar.pack(fill=tk.X, padx=10, pady=5)

        self.open_url_button = tk.Button(self.shortcut_bar, text="打开网页", command=self.load_url)
        self.open_url_button.pack(side=tk.LEFT, padx=5)

        self.save_button = tk.Button(self.shortcut_bar, text="保存", command=self.start_download)
        self.save_button.pack(side=tk.LEFT, padx=5)

        self.run_button = tk.Button(self.shortcut_bar, text="运行", command=self.run_html)
        self.run_button.pack(side=tk.LEFT, padx=5)

        '''# 添加高光快捷按钮
        highlight_button = tk.Button(self.shortcut_bar, text="添加高光", command=self.api_add_custom_highlight)
        highlight_button.pack(side=tk.LEFT, padx=5)

        # 添加自定义元素快捷按钮
        add_element_button = tk.Button(self.shortcut_bar, text="添加元素", command=self.api_add_custom_element)
        add_element_button.pack(side=tk.LEFT, padx=5)'''

        self.url_entry = ttk.Entry(main_frame, width=100)
        self.url_entry.insert(0, "https://example.com")
        self.url_entry.pack(fill=tk.X, padx=10, pady=5)

        self.editor = scrolledtext.ScrolledText(
            main_frame,
            wrap=tk.WORD,
            font=('TkFixedFont', 12),
            undo=True,
            tabs=4
        )
        self.editor.pack(fill=tk.BOTH, expand=True)

        self.current_file_progress_bar = ttk.Progressbar(main_frame, orient="horizontal", length=200, mode="determinate")
        self.current_file_progress_bar.pack(side=tk.LEFT, padx=10, pady=10)

        self.current_file_eta_label = tk.Label(main_frame, text="00:00:00")
        self.current_file_eta_label.pack(side=tk.LEFT, padx=10, pady=10)

        self.total_progress_bar = ttk.Progressbar(main_frame, orient="horizontal", length=300, mode="determinate")
        self.total_progress_bar.pack(side=tk.LEFT, padx=10, pady=10)

        self.total_percentage_label = tk.Label(main_frame, text="0%")
        self.total_percentage_label.pack(side=tk.LEFT, padx=10, pady=10)

        self.total_eta_label = tk.Label(main_frame, text="00:00:00")
        self.total_eta_label.pack(side=tk.LEFT, padx=10, pady=10)

        self.editor.bind('<KeyRelease>', self._schedule_highlight)

    def _setup_highlight(self):
        for tag, color in {
            'tag': 'blue',
            'attr': 'green',
            'value': 'red',
            'comment': 'gray',
            'doctype': 'purple'
        }.items():
            self.editor.tag_configure(tag, foreground=color)

        self.highlight_rules = [
            (r'<!--.*?-->', 'comment'),
            (r'<!DOCTYPE.*?>', 'doctype'),
            (r'<\/?\w+', 'tag'),
            (r'\b\w+=', 'attr'),
            (r'".*?"', 'value'),
            (r"'.*?'", 'value')
        ]

    def _schedule_highlight(self, event=None):
        if hasattr(self, 'highlight_id'):
            self.window.after_cancel(self.highlight_id)
        self.highlight_id = self.window.after(500, self._do_highlight)

    def _do_highlight(self):
        start = "1.0"
        end = tk.END
        text = self.editor.get(start, end)
        self.editor.tag_remove('tag', start, end)
        self.editor.tag_remove('attr', start, end)
        self.editor.tag_remove('value', start, end)
        self.editor.tag_remove('comment', start, end)
        self.editor.tag_remove('doctype', start, end)

        for pattern, tag in self.highlight_rules:
            matches = re.finditer(pattern, text)
            for match in matches:
                start_index = f"1.0+{match.start()}c"
                end_index = f"1.0+{match.end()}c"
                self.editor.tag_add(tag, start_index, end_index)

    def load_url(self):
        url = self.url_entry.get()
        try:
            response = urlopen(url)
            html_content = response.read().decode('utf-8', errors='ignore')
            self.editor.delete("1.0", tk.END)
            self.editor.insert(tk.END, html_content)
            self._do_highlight()
        except Exception as e:
            messagebox.showerror("错误", f"无法打开网页: {e}")

    def start_download(self):
        url = self.url_entry.get()
        html_content = self.editor.get("1.0", tk.END)
        save_dir = filedialog.askdirectory()
        if save_dir:
            downloader = ResourceDownloader(url, html_content, self.log, save_dir)
            modified_html = downloader.process_resources()
            html_file_path = os.path.join(save_dir, "index.html")
            with open(html_file_path, 'w', encoding='utf-8') as f:
                f.write(modified_html)
            messagebox.showinfo("完成", f"保存成功: {html_file_path}")
            self.update_progress_bars(downloader)

    def update_progress_bars(self, downloader):
        if downloader.total_count > 0:
            total_percentage = (downloader.success_count + downloader.failure_count) / downloader.total_count * 100
            self.total_progress_bar['value'] = total_percentage
            self.total_percentage_label.config(text=f"{total_percentage:.2f}%")

            current_percentage = downloader.current_file_percentage
            self.current_file_progress_bar['value'] = current_percentage
            self.current_file_eta_label.config(text=self.calculate_eta(downloader))

            if downloader.success_count + downloader.failure_count < downloader.total_count:
                self.window.after(1000, self.update_progress_bars, downloader)

    def calculate_eta(self, downloader):
        if downloader.current_file_start_time and downloader.current_file_bytes_downloaded > 0:
            elapsed_time = time.time() - downloader.current_file_start_time
            if downloader.current_file_percentage > 0:
                total_time = elapsed_time / (downloader.current_file_percentage / 100)
                remaining_time = total_time - elapsed_time
                return time.strftime("%H:%M:%S", time.gmtime(remaining_time))
        return "00:00:00"

    def run_html(self):
        html_content = self.editor.get("1.0", tk.END)
        temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.html')
        temp_file.write(html_content)
        temp_file.close()
        webbrowser.open_new_tab(f"file://{temp_file.name}")

    def save_and_run(self):
        self.start_download()
        self.run_html()

    def save_workstation(self):
        file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON Files", "*.json")])
        if file_path:
            workstation_data = {
                "url": self.url_entry.get(),
                "html_content": self.editor.get("1.0", tk.END)
            }
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(workstation_data, f, ensure_ascii=False, indent=4)
            messagebox.showinfo("保存成功", f"工作站已保存到 {file_path}")
            self.current_workstation = file_path

    def open_workstation(self):
        file_path = filedialog.askopenfilename(filetypes=[("JSON Files", "*.json")])
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    workstation_data = json.load(f)
                self.url_entry.delete(0, tk.END)
                self.url_entry.insert(0, workstation_data.get("url", ""))
                self.editor.delete("1.0", tk.END)
                self.editor.insert(tk.END, workstation_data.get("html_content", ""))
                self._do_highlight()
                messagebox.showinfo("打开成功", f"工作站已从 {file_path} 打开")
                self.current_workstation = file_path
            except Exception as e:
                messagebox.showerror("错误", f"无法打开工作站: {e}")

    def start_download_all_links(self):
        url = self.url_entry.get()
        html_content = self.editor.get("1.0", tk.END)
        save_dir = filedialog.askdirectory()
        if save_dir:
            downloader = ResourceDownloader(url, html_content, self.log, save_dir)
            threading.Thread(target=downloader.process_resources).start()

    def log(self, message, level="normal", start_index=None, end_index=None):
        if start_index and end_index:
            self.log_text.delete(start_index, end_index)
        else:
            self.log_text.insert(tk.END, message + '\n', level)
            self.log_text.see(tk.END)
            return self.log_text.index(tk.END + "-1 lines")

    def load_plugins(self):
        plugin_dir = "plugins"
        if not os.path.exists(plugin_dir):
            os.makedirs(plugin_dir)

        # 读取插件启用状态
        enabled_plugins = self.load_plugin_settings()

        for filename in os.listdir(plugin_dir):
            if filename.endswith(".py") and filename in enabled_plugins:
                plugin_path = os.path.join(plugin_dir, filename)
                try:
                    spec = importlib.util.spec_from_file_location(filename[:-3], plugin_path)
                    plugin = importlib.util.module_from_spec(spec)
                    spec.loader.exec_module(plugin)
                    if hasattr(plugin, 'plugin_entry'):
                        plugin.plugin_entry(self)
                except Exception as e:
                    messagebox.showerror("插件加载错误", f"加载插件 {filename} 时出错: {e}")

    def open_settings(self):
        # 创建设置窗口
        settings_window = tk.Toplevel(self.window)
        settings_window.title("设置")
        settings_window.geometry("800x600")

        # 左侧导航栏
        nav_frame = tk.Frame(settings_window, width=200)
        nav_frame.pack(side=tk.LEFT, fill=tk.Y)

        # 插件设置按钮
        plugin_settings_button = tk.Button(nav_frame, text="插件设置", command=lambda: self.show_plugin_settings(settings_window))
        plugin_settings_button.pack(pady=10)

        # 右侧内容区域
        self.content_frame = tk.Frame(settings_window)
        self.content_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        # 默认显示插件设置
        self.show_plugin_settings(settings_window)

    def show_plugin_settings(self, settings_window):
        # 清空内容区域
        for widget in self.content_frame.winfo_children():
            widget.destroy()

        # 插件列表
        plugin_dir = "plugins"
        if not os.path.exists(plugin_dir):
            os.makedirs(plugin_dir)

        plugin_files = [filename for filename in os.listdir(plugin_dir) if filename.endswith(".py")]
        enabled_plugins = self.load_plugin_settings()

        # 复选框变量
        checkbox_vars = {}
        for plugin_file in plugin_files:
            var = tk.IntVar()
            if plugin_file in enabled_plugins:
                var.set(1)
            checkbox_vars[plugin_file] = var
            checkbox = tk.Checkbutton(self.content_frame, text=plugin_file, variable=var)
            checkbox.pack()

        # 保存按钮
        save_button = tk.Button(self.content_frame, text="保存", command=lambda: self.save_plugin_settings(checkbox_vars, settings_window))
        save_button.pack(anchor=tk.SE, padx=20, pady=20)

    def load_plugin_settings(self):
        settings_file = "plugin_settings.json"
        if os.path.exists(settings_file):
            try:
                with open(settings_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except Exception:
                pass
        return []

    def save_plugin_settings(self, checkbox_vars, settings_window):
        enabled_plugins = []
        for plugin_file, var in checkbox_vars.items():
            if var.get() == 1:
                enabled_plugins.append(plugin_file)

        # 保存设置
        settings_file = "plugin_settings.json"
        with open(settings_file, 'w', encoding='utf-8') as f:
            json.dump(enabled_plugins, f, ensure_ascii=False, indent=4)

        # 重启程序
        settings_window.destroy()
        self.window.destroy()
        python = sys.executable
        os.execl(python, python, *sys.argv)

    def setup_shortcuts(self):
        # 添加自定义快捷键
        self.window.bind_all("<Control-h>", lambda event: self.api_add_custom_highlight())
        self.window.bind_all("<Control-e>", lambda event: self.api_add_custom_element())

    def api_add_custom_highlight(self):
        """API: 添加自定义高光"""
        start_index = self.editor.index(tk.SEL_FIRST)
        end_index = self.editor.index(tk.SEL_LAST)
        if start_index and end_index:
            self.editor.tag_add("custom_highlight", start_index, end_index)
            self.editor.tag_configure("custom_highlight", foreground="red")

    def api_add_custom_element(self):
        """API: 添加自定义元素"""
        element = simpledialog.askstring("添加元素", "请输入要添加的元素内容:")
        if element:
            self.editor.insert(tk.INSERT, element)


if __name__ == "__main__":
    app = EnhancedHTMLViewer()

    
